﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace LightCAD.MathLib
{

    public partial class GeoUtils
    {
        public static bool IsPointInPolygon(Vector2 point, Vector2[] polygon)
        {
            int polygonLength = polygon.Length, i = 0;
            bool inside = false;
            // x, y for tested point.
            double pointX = point.X, pointY = point.Y;
            // start / end point for the current polygon segment.
            double startX, startY, endX, endY;
            Vector2 endPoint = polygon[polygonLength - 1];
            endX = endPoint.X;
            endY = endPoint.Y;
            while (i < polygonLength)
            {
                startX = endX; startY = endY;
                endPoint = polygon[i++];
                endX = endPoint.X; endY = endPoint.Y;
                //
                inside ^= (endY > pointY ^ startY > pointY) /* ? pointY inside [startY;endY] segment ? */
                          && /* if so, test if it is under the segment */
                          ((pointX - endX) < (pointY - endY) * (startX - endX) / (startY - endY));
            }
            return inside;
        }

        public static bool IsConvexPolygon(Vector2[] points)
        { 
            int vertexCount = points.Length;

            if (vertexCount < 3) // 多边形至少需要3个顶点才能判断凸性
                return false;

            bool isClockwise = false;
            bool isCounterClockwise = false;

            for (int i = 0; i < vertexCount; i++)
            {
                Vector2 currentVertex = points[i];
                Vector2 nextVertex = points[(i + 1) % vertexCount];
                Vector2 previousVertex = points[(i - 1 + vertexCount) % vertexCount];

                Vector2 currentEdge = nextVertex - currentVertex;
                Vector2 previousEdge = currentVertex - previousVertex;

                double crossProduct = Vector2.Cross(previousEdge, currentEdge);

                if (crossProduct < 0)
                    isCounterClockwise = true;
                else if (crossProduct > 0)
                    isClockwise = true;

                if (isClockwise && isCounterClockwise)
                    return false; // 多边形同时存在顺时针和逆时针方向，不是凸多边形
            }
            return true;
        }

        public static double PointDistanceToLine(Vector2 point,Vector2 start, Vector2 end)
        {
            // 计算直线上的向量V
            Vector2 v = end - start;
            // 计算从P1到P的向量W
            Vector2 w = point - start;

            // 计算投影向量Proj
            double dotProduct = Vector2.Dot(w, v);
            var proj = (dotProduct / v.LengthSq()) * v;

            // 计算距离d
            Vector2 diff = w - proj;
            return diff.Length();
        }

        public static bool IsCircleInPolygon(Vector2 center, double radius, Vector2[] polygon)
        {

            for (int i = 0; i < polygon.Length; i++)
            {
                int j = (i + 1) % polygon.Length;

                if (CircleIntersectLine(center, radius, polygon[i], polygon[j]))
                    return false;
            }
            return IsPointInPolygon(center, polygon);

        }

        public static bool CircleIntersectLine(Vector2 center, double radius, Vector2 p1, Vector2 p2)
        {
            Vector2 v = p2 - p1;
            Vector2 w = center - p1;

            double t = Vector2.Dot(w, v) / Vector2.Dot(v, v);

            if (t < 0 || t > 1)
                return false;

            Vector2 projection = p1 + t * v;
            double distanceSquared = (center - projection).LengthSq();

            return distanceSquared <= radius * radius;
        }

        public static bool IsPolygonIntersectCircle(Vector2[] polygon, Vector2 center, double radius)
        {
            int count = polygon.Length;

            for (int i = 0; i < count; i++)
            {
                var p1 = polygon[i];
                var p2 = polygon[(i + 1) % count];

                if (IsLineIntersectCircle(p1, p2, center, radius))
                {
                    return true;
                }
            }

            return IsPointInPolygon(center,polygon);
        }

        public static bool IsLineIntersectCircle(Vector2 p1, Vector2 p2, Vector2 center, double radius)
        {
            double d1 = Vector2.Distance(center, p1);
            double d2 = Vector2.Distance(center, p2);

            if (d1 <= radius && d2 <= radius)
            {
                return false;//包含不是相交
            }
            //直线穿过圆弧一个交点
            if((d1<=radius && d2>=radius)|| (d1 >= radius && d2 <= radius))
            {
                return true;
            }

            Vector2 v = p2 - p1;
            Vector2 w = center - p1;

            double dotProduct = Vector2.Dot(w, v);
            double lengthSquared =  v.LengthSq();

            if (dotProduct < 0 || dotProduct > lengthSquared)
            {
                return false;
            }

            double distanceToLineSquared = w.LengthSq() - dotProduct * dotProduct / lengthSquared;

            return distanceToLineSquared <= radius * radius;
        }

        public static bool IsPolygonIntersectLine(Vector2[] polygon, Vector2 start, Vector2 end)
        {
            int count = polygon.Length;
            if (polygon.Last().Equals(polygon.First()))
            {
                count -= 1;
            }

            for (int i = 0; i < count; i++)
            {
                var p1 = polygon[i];
                var p2 = polygon[(i + 1) % count];

                if (IsLineIntersectLine(p1, p2, start, end))
                {
                    return true;
                }
            }

            return false;
        }

        public static bool IsPolygonIntersectArc(Vector2[] polygon, Arc2d arc)
        {
            int count = polygon.Length;

            for (int i = 0; i < count; i++)
            {
                var p1 = polygon[i];
                var p2 = polygon[(i + 1) % count];

                if (IsLineIntersectArc(p1, p2, arc))
                {
                    return true;
                }
            }

            return false;
        }

        public static bool IsPolygonContainsLine(Vector2[] polygon, Vector2 start, Vector2 end)
        {
            int count = polygon.Length;
            if (polygon.Last().Equals(polygon.First()))
            {
                count -= 1;
            }
            for (int i = 0; i < count; i++)
            {
                var p1 = polygon[i];
                var p2 = polygon[(i + 1) % count];
                //任何一条边和线段相交，则为不包含
                if (IsLineIntersectLine(p1, p2, start, end))
                {
                    return false;
                }
            }
            //任意一点在多边形内，即为包含，反之为不包含
            return IsPointInPolygon(start,polygon);
        }

        public static bool IsPolygonContainsPolygon(Vector2[] polygon1, Vector2[] polygon2)
        {
            int count = polygon1.Length;
            if (polygon1.Last().Equals(polygon1.First()))
            {
                count -= 1;
            }

            for (int i = 0; i < count; i++)
            {
                var p1 = polygon1[i];
                var p2 = polygon1[(i + 1) % count];
                //任何一条边和多边形相交或者不在多边形内，则为不包含
                if (!IsPolygonContainsLine(polygon2, p1, p2))
                {
                    return false;
                }
            }


            return true;
        }

        public static bool IsLineIntersectLine(Vector2 p1, Vector2 p2, Vector2 p3, Vector2 p4)
        {
            double denominator = ((p4.Y - p3.Y) * (p2.X - p1.X)) - ((p4.X - p3.X) * (p2.Y - p1.Y));
            double numerator1 = ((p4.X - p3.X) * (p1.Y - p3.Y)) - ((p4.Y - p3.Y) * (p1.X - p3.X));
            double numerator2 = ((p2.X - p1.X) * (p1.Y - p3.Y)) - ((p2.Y - p1.Y) * (p1.X - p3.X));

            if (denominator == 0)
            {
                if (numerator1 == 0 && numerator2 == 0)
                {
                    // 线段共线
                    return IsPointOnLine(p1, p2, p3) || IsPointOnLine(p1, p2, p4);
                }

                // 线段平行不相交
                return false;
            }

            double r = numerator1 / denominator;
            double s = numerator2 / denominator;

            return r >= 0 && r <= 1 && s >= 0 && s <= 1;
        }

        public static bool IsPointOnLine(Vector2 start, Vector2 end, Vector2 point)
        {
            double minX = Math.Min(start.X, end.X);
            double maxX = Math.Max(start.X, end.X);
            double minY = Math.Min(start.Y, end.Y);
            double maxY = Math.Max(start.Y, end.Y);

            return point.X >= minX && point.X <= maxX && point.Y >= minY && point.Y <= maxY;
        }


        // 判断一个线段是否和圆弧相交
        public static bool IsLineIntersectArc(Vector2 p1, Vector2 p2, Arc2d arc)
        {
            // 确保 p1.x <= p2.x
            if (p1.X > p2.X)
            {
                var temp = p1.Clone();
                p1 = p2;
                p2 = temp;
            }
            // 简化圆弧的相关变量表示
            double a = arc.Center.X;
            double b = arc.Center.Y;
            double r = arc.Radius;
            double theta1 = arc.StartAngle;
            double theta2 = arc.EndAngle;
            // 第一步（粗略判断）：将线段当成直线，将圆弧当成圆，如果直线和圆不相交，则线段和圆弧必然不相交，否则进行下一步判断
            // 1.1 根据公式，计算直线方程 y=kx+c 中的 k 和 c
            double k = (p1.Y - p2.Y) / (p1.X - p2.X);
            double c = p1.Y - (p1.Y - p2.Y) / (p1.X - p2.X) * p1.X;
            // 1.2 根据韦达定理判断式（6）是否存在实数根
            double d = c - b;
            double varDelta = Math.Pow(2 * k * d - 2 * a, 2) - 4 * (1 + k * k) * (a * a + d * d - r * r);
            if (varDelta >= 0)
            {
                // 第二步：判断直线和圆的两个交点是否在线段上，如果不在，说明线段和圆弧必然不相交，否则进行下一步判断
                // 2.1 计算两个交点的坐标(x1,y1)和(x2,y2)
                double x1 = (2 * a - 2 * k * d + Math.Sqrt(varDelta)) / (2 * (1 + k * k));
                double x2 = (2 * a - 2 * k * d - Math.Sqrt(varDelta)) / (2 * (1 + k * k));
                double y1 = k * x1 + c;
                double y2 = k * x2 + c;
                // 2.2 判断两个交点是否相等
                if (varDelta == 0)
                {
                    // 两个交点相等，只需要判断一个就好
                    return IsLineIntersectArcAuxiliaryFunction(p1, p2, a, b, theta1, theta2, x1, y1);
                }
                else
                {
                    // 两个交点不相等，分别进行判断，只要其中一个是线段和圆弧的交点就返回 true
                    return IsLineIntersectArcAuxiliaryFunction(p1, p2, a, b, theta1, theta2, x1, y1) || IsLineIntersectArcAuxiliaryFunction(p1, p2, a, b, theta1, theta2, x2, y2);
                }
            }
            return false;
        }

        // lineIsIntersectArc 的辅助函数：接着第二步判断
        private static bool IsLineIntersectArcAuxiliaryFunction(Vector2 p1, Vector2 p2, double a, double b, double theta1, double theta2, double x1, double y1)
        {
            var eps = 0.0000001;
            // 2.3 判断交点是否在线段上
            if (p1.X <= x1 && x1 <= p2.X)
            {
                // 第三步：交点(x1,y1)在线段上，再判断该点是否在圆弧上，如果在，则说明线段和圆弧相交，且交点为(x1,y1)
                // 3,1 计算 theta ，并转化为角度值
                double theta = Math.Atan((y1 - b) / (x1 - a)) / Math.PI * 180.0;
                // 3.2 修正角度值，确保 theta1 <= theta2
                if (theta1 > theta2)
                {
                    if (theta2 >= 0)
                    {
                        theta1 -= 360;
                    }
                    else
                    {
                        theta2 += 360;
                    }
                }
                if (theta1 < 0 && theta2 < 0)
                {
                    theta1 += 360;
                    theta2 += 360;
                }
                // 3.3 修正 tan 的角度
                if (x1 < a)
                {
                    theta += 180;
                }
                // 3.4 在端点时，由于精度误差，有时可能会出现判断出错 (即出现 10.000000001 > 10 的情况)，故以极小的误差接受(x1,y1)为圆弧的某个端点
                if (Math.Abs(theta1 - theta) < eps || Math.Abs(theta2 - theta) < eps)
                {
                    return true;
                }
                // 3.5 判断 theta 是否在圆弧范围内，如果在则(x1,y1)是线段和圆弧的交点，否则不是
                return theta1 <= theta && theta <= theta2;
            }
            return false;
        }

        public static double PointToLineDistance(Vector2 p1,Vector2 p2,Vector2 p, out Vector2 prjPoint, out double dirDistance)
        {
            var direction = (p2 - p1).Normalize();
            dirDistance = Vector2.Dot((p - p1), direction);
            prjPoint = p1 + direction * dirDistance;
            return Vector2.Distance(prjPoint, p);
        }
        public static int AngleAccuracy = 4;
        public static bool AlmostEqual(double d1, double d2)
        {
            return AlmostEqual(d1, d2, AngleAccuracy);
        }
        public static bool AlmostEqual(double d1, double d2, int accuracy)
        {
            return Math.Round(d1, accuracy) == Math.Round(d2, accuracy);
        }
    }
}